﻿#include "stdafx.h"
#include "qg/qgasv.h"

//////////////////////////////////////////////////////////////////////////
// 스터브
KIMPL_SUBCLASS_ABSTRACT(qgStub, "RenderStub");

namespace _stub
{
	static char StubClassName[64] = "__qg_stub_class__";

	extern qgStub* NewStub(const tpl::Rect& area, kint flags);
	extern qgStub* NewStubVirtual(kpointer handle);

	//
	class FakeHandlerStub : public qgStub
	{
	public:
		kuint AddHandlerForce(qgSev sev, const qgSevFunc* func)
		{
			SevHash* h = _els[sev];

			if (!h)
			{
				h = new SevHash();
				_els[sev] = h;
			}

			kuint index = (_elx & 0x00FFFFFF) | (sev << 24);
			h->Set(index, *func);

			if ((++_elx) > 0x00FFFFFF)
				_elx = 10;

			return index;
		}
	};
}

qgStub::qgStub()
: _flags(0)
, _insts(0)
, _delay(10)
, _btext("QG")
, _elx(10)
{
	_timer = k_timer_new();
#if !_SB_WINDOWS_ || TRUE
	k_timer_set_manual(_timer, true);	// 유닉스에서 문제가 생기는 것을 막기 위함
#endif

	// 마우스 더블크릭
	_mouse.mclick.limmove = 5 * 5;		// 제한 이동 거리 제곱
	_mouse.mclick.limtick = 500;		// 제한 클릭 시간
}

qgStub::~qgStub()
{
	kForEach(qgWriter* l, _svwt)
		l->Dispose();

	for (ksize_t i = 0; i < QGSLCS_MAX_VALUE; i++)
	{
		kForEach(qgReader* l, _svrd[i])
			l->Dispose();
	}

	for (ksize_t i = 0; i < QGSEV_MAX_VALUE; i++)
	{
		if (_els[i])
			delete _els[i];
	}

	k_timer_delete(_timer);
}

//
kbool qgStub::Close()
{
	return true;
}

//
kbool qgStub::Layout()
{
	return true;
}

//
kbool qgStub::Poll()
{
	return false;
}

//
kbool qgStub::Run()
{
	if (!K_OFLAG(_insts, QGSDI_VIRTUAL))
	{
		if (Poll() || K_OFLAG(_insts, QGSDI_EXIT))
		{
			// 스터브의 실행이 끝남
			return false;
		}
	}

	k_timer_update(_timer);

	_runfps = (float)k_timer_get_fps(_timer);
	_runtime = (float)k_timer_get_run(_timer);
	_advance[0] = (float)k_timer_get_adv(_timer);
	_advance[2] = _advance[1];

	if (!K_OFLAG(_insts, QGSDI_PAUSE))
	{
		_advance[1] = _advance[0];
		_advance[3] = _advance[0];
	}

	if (K_OFLAG(_insts, QGSDI_GOPU))
		k_sleep(K_OFLAG(_flags, QGSDF_LATENCY) || !K_OFLAG(_insts, QGSDI_ACTIVE | QGSDI_VIRTUAL) ? _delay : 1);
	else if (K_OFLAG(_flags, QGSDF_LATENCY) || !K_OFLAG(_insts, QGSDI_ACTIVE | QGSDI_VIRTUAL))
		k_sleep(_delay);

	return true;
}

//
kbool qgStub::GetMouse(qgUimMouse* v)
{
	k_return_value_if_fail(v, false);

	*v = _mouse;

	return true;
}

//
kbool qgStub::GetKeyTable(qgUimKey* k)
{
	k_return_value_if_fail(k, false);

	*k = _key;

	return true;
}

//
kbool qgStub::IsKeyDown(kbyte key)
{
	return _key.key[key] != 0;
}

//
kbool qgStub::GetControllerInput(kint /*slot*/, qgUimControllerInput* /*inf*/)
{
	return false;
}

//
kbool qgStub::GetControllerInfo(kint /*slot*/, qgUimControllerInfo* /*stt*/)
{
	return false;
}

//
kbool qgStub::GetText(char* buffer, kint size)
{
	k_return_value_if_fail(buffer && size > 0, false);

	k_strncpy(buffer, _btext.Data(), K_MIN(size, _btext.Length()));

	return true;
}

//
kvoid qgStub::SetKeyDown(kbyte key, kbool down)
{
	_key.key[key] = down;
}

//
kbool qgStub::SetControllerEnable(kbool /*v*/)
{
	return false;
}

//
kbool qgStub::SetControllerVibration(kint /*slot*/, const qgUimControllerVibration& /*v*/)
{
	return false;
}

//
kvoid qgStub::SetDropEnable(kbool /*v*/)
{

}

//
kvoid qgStub::SetAcsEnable(kbool /*v*/)
{

}

//
kvoid qgStub::SetImeEnable(kbool v)
{
}

//
kbool qgStub::SetText(const char* buffer)
{
	_btext = buffer;

	return true;
}

//
kvoid qgStub::SetLimitSize(kint width, kint height)
{
	_limsize.Set(K_MAX(width, 0), K_MAX(height, 0));
}

//
kvoid qgStub::HoldMouse(kbool /*v*/)
{

}

//
kintptr qgStub::SystemProc(const qgSevSystem& /*e*/)
{
	return 0;
}

//
kuint qgStub::AddSystemHandler(const qgSevSystemFunc& func)
{
	k_return_value_if_fail(func, 0);
	return static_cast<_stub::FakeHandlerStub*>(this)->AddHandlerForce(QGSEV_SYSTEM, (const qgSevFunc*)&func);
}

//
kuint qgStub::AddLayoutHandler(const qgSevLayoutFunc& func)
{
	k_return_value_if_fail(func, 0);
	return static_cast<_stub::FakeHandlerStub*>(this)->AddHandlerForce(QGSEV_LAYOUT, (const qgSevFunc*)&func);
}

//
kuint qgStub::AddActiveHandler(const qgSevActiveFunc& func)
{
	k_return_value_if_fail(func, 0);
	return static_cast<_stub::FakeHandlerStub*>(this)->AddHandlerForce(QGSEV_ACTIVE, (const qgSevFunc*)&func);
}

//
kuint qgStub::AddCursorHandler(const qgSevCursorFunc& func)
{
	k_return_value_if_fail(func, 0);
	return static_cast<_stub::FakeHandlerStub*>(this)->AddHandlerForce(QGSEV_CURSOR, (const qgSevFunc*)&func);
}

//
kuint qgStub::AddDropHandler(const qgSevDropFunc& func)
{
	k_return_value_if_fail(func, 0);
	return static_cast<_stub::FakeHandlerStub*>(this)->AddHandlerForce(QGSEV_DROPFILE, (const qgSevFunc*)&func);
}

//
kuint qgStub::AddMouseMoveHandler(const qgSevMouseMoveFunc& func)
{
	k_return_value_if_fail(func, 0);
	return static_cast<_stub::FakeHandlerStub*>(this)->AddHandlerForce(QGSEV_MOUSEMOVE, (const qgSevFunc*)&func);
}

//
kuint qgStub::AddMouseUpHandler(const qgSevMouseButtonFunc& func)
{
	k_return_value_if_fail(func, 0);
	return static_cast<_stub::FakeHandlerStub*>(this)->AddHandlerForce(QGSEV_MOUSEUP, (const qgSevFunc*)&func);
}

//
kuint qgStub::AddMouseDownHandler(const qgSevMouseButtonFunc& func)
{
	k_return_value_if_fail(func, 0);
	return static_cast<_stub::FakeHandlerStub*>(this)->AddHandlerForce(QGSEV_MOUSEDOWN, (const qgSevFunc*)&func);
}

//
kuint qgStub::AddMouseDoubleHandler(const qgSevMouseButtonFunc& func)
{
	k_return_value_if_fail(func, 0);
	return static_cast<_stub::FakeHandlerStub*>(this)->AddHandlerForce(QGSEV_MOUSEDOUBLE, (const qgSevFunc*)&func);
}

//
kuint qgStub::AddMouseWheelHandler(const qgSevMouseWheelFunc& func)
{
	k_return_value_if_fail(func, 0);
	return static_cast<_stub::FakeHandlerStub*>(this)->AddHandlerForce(QGSEV_MOUSEWHEEL, (const qgSevFunc*)&func);
}

//
kuint qgStub::AddKeyDownHandler(const qgSevKeyFunc& func)
{
	k_return_value_if_fail(func, 0);
	return static_cast<_stub::FakeHandlerStub*>(this)->AddHandlerForce(QGSEV_KEYDOWN, (const qgSevFunc*)&func);
}

//
kuint qgStub::AddKeyUpHandler(const qgSevKeyFunc& func)
{
	k_return_value_if_fail(func, 0);
	return static_cast<_stub::FakeHandlerStub*>(this)->AddHandlerForce(QGSEV_KEYUP, (const qgSevFunc*)&func);
}

//
kuint qgStub::AddCharHandler(const qgSevCharFunc& func)
{
	k_return_value_if_fail(func, 0);
	return static_cast<_stub::FakeHandlerStub*>(this)->AddHandlerForce(QGSEV_CHAR, (const qgSevFunc*)&func);
}

//
kbool qgStub::RemoveHandler(kuint evid)
{
	k_return_value_if_fail(evid != 0, false);

	qgSev sev = (qgSev)((evid >> 24) & 0xFF);
	k_return_value_if_fail((ksize_t)sev < QGSEV_MAX_VALUE, false);

	SevHash* h = _els[sev];
	k_return_value_if_fail(h && h->IsHave(), false);

	return h->Remove(evid);
}

//
kbool qgStub::CanInvokeHandler(qgSev sev)
{
	return (ksize_t)sev < QGSEV_MAX_VALUE && _els[sev] && _els[sev]->IsHave();
}

//
kbool qgStub::InvokeHandler(qgSev sev, kpointer arg)
{
	k_return_value_if_fail((ksize_t)sev < QGSEV_MAX_VALUE, false);

	SevHash* h = _els[sev];
	k_return_value_if_fail(h && h->IsHave(), false);

	kForEach(auto& item, *h)
	{
		auto& ev = item.second;

		if (!ev(arg))
			return false;
	}

	return true;
}

//
kbool qgStub::UnsafeInvokeHandler(qgSev sev, kpointer arg)
{
	SevHash* h = _els[sev];

	kForEach(auto& item, *h)
	{
		auto& ev = item.second;

		if (!ev(arg))
			return false;
	}

	return true;
}

//
kvoid qgStub::RegisterReader(qgSlcs slt, qgReader* ptr)
{
	k_return_if_fail((ksize_t)slt < QGSLCS_MAX_VALUE);
	k_return_if_fail(ptr != NULL);

	_svrd[slt].Add(ptr);
}

//
kbool qgStub::PerformRead(kObj* ctnr, kconstpointer data, kint size, kint flags, qgSlcs slt /*= QGSLCS_FORCE*/)
{
	if (slt == QGSLCS_FORCE)
	{
		for (ksize_t i = 0; i < QGSLCS_MAX_VALUE; i++)
		{
			kForEach(qgReader* l, _svrd[i])
			{
				if (l->Test(data, size) && l->Read(ctnr, data, size, flags))
					return true;
			}
		}
	}
	else if ((ksize_t)slt < QGSLCS_MAX_VALUE)
	{
		kForEach(qgReader* l, _svrd[slt])
		{
			if (l->Test(data, size) && l->Read(ctnr, data, size, flags))
				return true;
		}
	}

	return false;
}

//
kvoid qgStub::RegisterWriter(qgWriter* ptr)
{
	k_return_if_fail(ptr != NULL);

	_svwt.Add(ptr);
}

//
kbool qgStub::PerformWrite(kStream* st, kObj* ctnr, kint flags)
{
	const char* desc = st->GetDesc();
	k_return_value_if_fail(desc, false);

	char* psz = k_strrchr(desc, '.');
	k_return_value_if_fail(psz, false);

	tpl::BlueStr<64> bs(psz + 1);

	kForEach(qgWriter* l, _svwt)
	{
		if (l->Test(bs.Data()) && l->Write(st, ctnr, flags))
			return true;
	}

	return false;
}

//
bool qgStub::_ProcessMultiClick(kint button, kint method)
{
	if (method == QIMT_MOVE)
	{
		if (_mouse.mclick.tick > 0)
		{
			kint dx = _mouse.mclick.location.x - _mouse.point.x;
			kint dy = _mouse.mclick.location.y - _mouse.point.y;
			kint d = dx * dx + dy * dy;

			if (d > _mouse.mclick.limmove)
			{
				// 마우스가 _mouse.mclick.limmove 포인트 움직이면 두번 누르기 취소
				_mouse.mclick.tick = 0;
			}
		}
	}
	else if (method == QIMT_DOWN)
	{
		if (_mouse.mclick.tick == 0)
		{
			_mouse.mclick.tick = k_gettick();
			_mouse.mclick.location = _mouse.point;
			_mouse.mclick.button = button;
		}
		else
		{
			if (_mouse.mclick.button == button)
			{
				kuint delta = k_gettick() - _mouse.mclick.tick;

				if (delta < _mouse.mclick.limtick)
				{
					// 더블클릭했음
					_mouse.mclick.tick = 0;
					return true;
				}
			}

			// 취소
			_mouse.mclick.tick = 0;
		}
	}
	else if (method == QIMT_UP)
	{
		// 할거 없네...
	}

	return false;
}

//
const char* qgStub::GetInternalClassName()
{
	return _stub::StubClassName;
}

//
kvoid qgStub::SetInternalClassName(const char* name)
{
	if (name)
		k_strncpy(_stub::StubClassName, name, 63);
}

//
qgStub* qgStub::New(const kRt& bound, const char* text, kint flags)
{
	qgStub* stub = _stub::NewStub(bound, flags);

	if (stub && text)
		stub->SetText(text);

	return stub;
}

//
qgStub* qgStub::New(kint width, kint height, const char* text, kint flags)
{
	qgStub* stub = _stub::NewStub(tpl::Rect(0, 0, width, height), flags);

	if (stub && text)
		stub->SetText(text);

	return stub;
}

//
qgStub* qgStub::NewVirtual(kpointer handle)
{
	qgStub* stub = _stub::NewStubVirtual(handle);

	return stub;
}
